Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Helpers for using rngs from other packages #333

Merged
merged 27 commits into from
Nov 12, 2021
Merged

Conversation

richfitz
Copy link
Member

@richfitz richfitz commented Nov 11, 2021

This PR implements helpers that I needed while helping Will with https://github.com/mrc-ide/viralload; if someone wants to use the dust RNGs from their package then they need a way of keeping the state alive easily. Here we add a little dust_rng_pointer class that does that, and which can be used with any of the 12 underlying generator types, and which can be safely unpacked in user C++ code, at which point they can use the usual dust functions.

There's some interesting bits in here:

  • The pointers know what algorithm they are initialised with at runtime and can validate this at compile time even though that happens first.
  • One use I expect is that users will feed pointers generated this way into things like parallel::parLapply which will serialise the object, setting the pointer to NULL. To help with this I'm keeping a rarely synced copy of the state on the object. This will work with the most common use case which is to create an object then send it elsewhere. In the case where the object has ever been used this will error as the state will be out of date.

I've added a small vignette that explains how to use this for the single-process case. There's more work to get the multi-process case working that will build on this, but gets closer to #297 and I think will be best dealt with separately because this is already fairly large. Open to suggestions for good distributed problems on that issue for the docs, otherwise I'll probably do simple mcmc.

This PR builds on #330 and should be considered after that PR. For differences in the meantime see i325-normal-docs...i329-rng-helpers

Fixes #329

@codecov
Copy link

codecov bot commented Nov 12, 2021

Codecov Report

Merging #333 (fda13a4) into master (568f4ec) will not change coverage.
The diff coverage is 100.00%.

❗ Current head fda13a4 differs from pull request most recent head 621e602. Consider uploading reports for the commit 621e602 to get more accurate results
Impacted file tree graph

@@            Coverage Diff             @@
##            master      #333    +/-   ##
==========================================
  Coverage   100.00%   100.00%            
==========================================
  Files           60        62     +2     
  Lines         3356      3485   +129     
==========================================
+ Hits          3356      3485   +129     
Impacted Files Coverage Δ
R/rng_pointer.R 100.00% <100.00%> (ø)
inst/include/dust/r/helpers.hpp 100.00% <100.00%> (ø)
inst/include/dust/r/random.hpp 100.00% <100.00%> (ø)
src/dust_rng.cpp 100.00% <100.00%> (ø)
src/dust_rng_pointer.cpp 100.00% <100.00%> (ø)
src/test_rng.cpp 100.00% <100.00%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 568f4ec...621e602. Read the comment docs.

@richfitz richfitz marked this pull request as ready for review November 12, 2021 11:43
@richfitz richfitz requested a review from johnlees November 12, 2021 11:43
Copy link
Member

@johnlees johnlees left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe also add to the vignette explaining the use of serialisation and sync?

R/rng_pointer.R Outdated
@@ -0,0 +1,70 @@
##' @title Create pointer to random number generator
##'
##' For external use, vignette coming soon.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Vignette in this PR?

// Start with the assumption that we'll pass in the R6 object, might
// write a simpler version later.
template <typename rng_state_type>
prng<rng_state_type>* rng_pointer_get(cpp11::environment obj,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possible useful reference for #271. If we wanted to add this and support saving and loading, see the serialisation examples:
https://pybind11.readthedocs.io/en/stable/advanced/classes.html#pickling-support
https://github.com/pybind/pybind11/blob/master/tests/test_pickling.cpp

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The R serialisation has no hooking functionality, it's super annoying

@@ -321,6 +321,8 @@ plain_output(readLines(file.path(path_pkg, "NAMESPACE")))

Finally, run `cpp11::cpp_register()` before compiling your package so that the relevant interfaces are created (`R/cpp11.R` and `cpp11/cpp11.cpp`). A similar process would likely work with Rcpp without any dependency on cpp11.

More interesting use with persistent streams is described in `vignette("rng_package.Rmd")`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure how clear 'persistent streams' is on its own. I think the use in the vignette below might be described (based on similar FFI packages) as 'using the dust RNG from C++', whereas another (this?) vignette might be 'using the dust RNG from R'

* We've added `[[cpp::linking_to(dust)]]` and included the dust random interface (`dust/r/random.hpp`)
* The first line of the function safely creates a pointer to the random state data. The template argument here (`<dust::random::xoshiro256plus_state>`) refers to the rng algorithm and matches `rng$algorithm`
* The second line extracts a reference to the first (C++ indexing starting at 0) random number stream - this pair of lines is roughly equivalent to `GetRNGstate()` except that that the random numbers do not come from some global source
* After that the listing proceeds as before proceeds as before, except there is no equivalent to `PutRNGstate()`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe explain again why this isn't needed (no global state to update back to)

Copy link
Member

@johnlees johnlees left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great, thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Helpers for standalone rng state manipulation
2 participants